
/**
 * Holds functions for dealing with the swarm display.
 */

#include <swarm.h>

#include <string.h>

GtkWidget *_draw_area;
GtkWidget *_window;
GdkPixmap *_back_buffer;

int _x_win;
int _y_win;

static gint sw_gtk_event_close(GtkWidget *window, 
			       GdkEvent *event, gpointer data){

  gtk_main_quit();
  sw_exit(0);

  // For the compiler.
  return 0;

}

static gboolean sw_gtk_event_configure(GtkWidget *widget, 
				       GdkEventConfigure *event){

  if (_back_buffer)
    g_object_unref(_back_buffer);

  _back_buffer = gdk_pixmap_new(widget->window,
				widget->allocation.width,
				widget->allocation.height,
				-1);

  gdk_draw_rectangle(_back_buffer,
		     widget->style->black_gc,
		     TRUE,
		     0, 0,
		     widget->allocation.width,
		     widget->allocation.height);
  
  _x_win = widget->allocation.width;
  _y_win = widget->allocation.height;

  return TRUE;

}

/* Redraw the screen from the backing pixmap */
static gboolean sw_gtk_event_expose(GtkWidget *widget, GdkEventExpose *event){

  gdk_draw_drawable(widget->window,
		    widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
		    _back_buffer,
		    event->area.x, event->area.y,
		    event->area.x, event->area.y,
		    event->area.width, event->area.height);

  return FALSE;

}

void _sw_make_display(){

  /* The widgit to hold our drawing space. */
  _draw_area = gtk_drawing_area_new();

  _window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_default_size(GTK_WINDOW(_window), _x_win, _y_win);

  /* Connect all the signals for the draw area. */
  g_signal_connect(G_OBJECT(_window), "delete_event", 
		   G_CALLBACK(sw_gtk_event_close), NULL);

  g_signal_connect(G_OBJECT(_draw_area), "configure_event", 
		   G_CALLBACK(sw_gtk_event_configure), NULL);

  g_signal_connect(G_OBJECT(_draw_area), "expose_event", 
		   G_CALLBACK(sw_gtk_event_expose), NULL);

  gtk_container_add(GTK_CONTAINER(_window), _draw_area);
  
  gtk_widget_set_events (_draw_area, GDK_EXPOSURE_MASK
			 | GDK_LEAVE_NOTIFY_MASK
			 | GDK_BUTTON_PRESS_MASK
			 | GDK_POINTER_MOTION_MASK
			 | GDK_POINTER_MOTION_HINT_MASK);

}

int sw_init_display(int *argc, char ***argv, int x, int y){

  /* Parse the user args if any. */
  gtk_init(argc, argv);

  _x_win = x;
  _y_win = y;

  /* Make the display. */
  _sw_make_display();

  /* Show the display. */
  gtk_widget_show(_draw_area);
  gtk_widget_show(_window);

  return 0;

}

void *_sw_gtk_main_handler(void *pthread_data){

  gdk_threads_init();
  gtk_main();
  g_thread_exit(NULL);
  return NULL;

}

pthread_t _sw_ui_thread;

void sw_start_display(){

  /* Spawn a thread to handle the gtk main loop. */
  g_thread_init(NULL);  
  g_thread_create(_sw_gtk_main_handler, NULL, TRUE, NULL);

}

void sw_flush_display(){

  //printf("Queueing draw...");
  gdk_threads_enter();
  gtk_widget_queue_draw(_window);
  gdk_threads_leave();
  //printf(" done\n");

}

GdkGC *gc;
double ins_size = 13;
char   _rand_init = 0;

void sw_display_insect(sw_insect *ins){

  gdk_threads_enter();

  if (gc == NULL)
    gc = gdk_gc_new(GDK_DRAWABLE(_back_buffer));

  if (!_rand_init){
    sw_init_rand(0, 100);
    _rand_init = 1;
  }

  /* Draw an insect to the back buffer. */
  gdk_gc_set_rgb_fg_color(gc, &ins->color);

  /* First make the velocity component of the insect line. */
  sw_xmm_vec vel;
  double mag;

  if (ins->vel.x == 0 && ins->vel.y == 0){
    // Randomly generate the velocity.
    vel.x = 50 - sw_random();
    vel.y = 50 - sw_random();
  } else {
    memcpy(&vel, &ins->vel, sizeof(vel));
  }

  mag = _sw_vec_dot(&vel, &vel);
  mag = sqrt(mag);
  mag = 1 / mag;
  _sw_vec_scale(&vel, &mag);
  _sw_vec_scale(&vel, &ins_size);

  gdk_draw_line(_back_buffer, gc, 
		(gint) ins->loc.x, (gint) ins->loc.y,
		(gint) ins->loc.x - vel.x, 
		(gint) ins->loc.y - vel.y);
  
  gdk_threads_leave();

}

GdkColor col;
int col_defined = 0;

void sw_clear_display(){

  gdk_threads_enter();
  
  if (!col_defined){
    gdk_color_parse("black", &col);
    col_defined = 1;
  }

  gdk_gc_set_rgb_fg_color(gc, &col);

  gdk_draw_rectangle(_back_buffer,
		     gc,
		     TRUE,
		     0, 0,
		     _x_win,
		     _y_win);
  
  gdk_threads_leave();

}

void sw_display_swarm(sw_swarm *sw){

  //printf("[swarm] Displaying swarm of size: %d\n", (int) sw->swarm_size);

  int i = 0;
  for ( ; i < sw->swarm_size; i++){
    sw_display_insect(sw->swarm[i]);
  }

}
